/*
 * LogNormalDistribution : Definition de la loi Log Normale
 *
 * @author
 * @version 0
 */
package br.unb.statistic.distribution;

import java.io.IOException;
import java.util.HashMap;
import br.unb.statistic.Mathematic;
import br.unb.statistic.InterPacketTimeGenerator;
import br.unb.statistic.RandomGenerator;
import br.unb.statistic.inference.Inference;
import br.unb.utils.Resource;

public class LogNormalDistribution extends GenericContinuousDistribution implements RandomGenerator, InterPacketTimeGenerator {

    /**
     * Equation de la densite: <BR>
     *  f(x)= exp(-( (log(x)-mu) / (sqrt(2)*sigma) )^2)/(sqrt(2*pi)*sigma)<BR>
     *     where mu = mean of a normally distributed variable <BR>
     *           sigma = standard deviation of a normally distributed variable<BR>
     *           x = value at which to evaluate the function, must be > 0 <BR> 
     */
    public static int MU = 0;
    public static int SIGMA = 1;

    public LogNormalDistribution() {
        paramsCount = 2;
        name = new String("LogNormal");
        paramNames = new String[paramsCount];
        paramNames[MU] = new String("mu");
        paramNames[SIGMA] = new String("sigma");
        params = new double[paramsCount];
        params[MU] = 1.0d;
        params[SIGMA] = 1.0d;
    }

    public LogNormalDistribution(double mu, double sigma) throws IllegalArgumentException {
        this();
        if (sigma > 0) {
            params[MU] = mu;
            params[SIGMA] = sigma;
        } else {
            throw new IllegalArgumentException(Resource.getString("LOGNORMALDISTRIBUTION.CONSTRUCTOR_SIGMA_<_0") + params[SIGMA]);
        }
    }

    public LogNormalDistribution(double[] params) throws IllegalArgumentException {
        this();
        if (hasValidParam(params)) {
            setParams(params);
        } else {
            throw new IllegalArgumentException(Resource.getString("LOGNORMALDISTRIBUTION.CONSTRUCTOR_SIGMA_<_0") + params[SIGMA]);
        }
    }

    public boolean hasValidParam(double[] desParams) {
        if (desParams[SIGMA] > 0) {
            return true;
        } else {
            return false;
        }
    }

    public void setParams(double[] params) throws IllegalArgumentException {
        if (params.length >= paramsCount && params[SIGMA] > 0) {
            this.params[MU] = params[MU];
            this.params[SIGMA] = params[SIGMA];
        } else {
            throw new IllegalArgumentException(Resource.getString("STD_DEV_L.T._0"));
        }
    }

    public void setParam(int idx, double p) throws IllegalArgumentException {
        if (idx < paramsCount && (idx != SIGMA || p > 0)) {
            params[idx] = p;
        } else {
            throw new IllegalArgumentException(Resource.getString("STD_DEV_L.T._0"));
        }
    }

    /**
     * Returns the value of the probability distribution function at x
     */
    public double pdf(double x) {
        if (x > 0) {
            return (1.0d / (params[SIGMA] * x * Math.sqrt(2.0d * Math.PI) *
                    Math.exp(Math.pow((Math.log(x) - params[MU]) / params[SIGMA], 2.0d) / 2.0d)));
        } else {
            return 0;
        }
    }

    /**
     * Returns the value of the cumulative distribution function at x
     */
    public double cdf(double x) {
        if (x > 0) {
            return (normalStandardCdf((Math.log(x) - params[MU]) / params[SIGMA]));
        } else {
            return 0d;
        }
    }

    /**
     * Return the value of Inverse Function at x 
     * @param x with x>=0 and x<=1
     */
    public double inverseCdf(double x) {
        return Math.exp(params[SIGMA] * Mathematic.invNormal(x) + params[MU]);
    }

    //   public double rand(){
    //  	return this.inverseCdf(random.nextDouble());
    //  }
    /**
     * Returns the mean of the distribution
     */
    public double mean() {
        return Math.exp(params[MU] + 0.5d * Math.pow(params[SIGMA], 2.0d));
    }

    /**
     * Returns the variance of the distribution
     */
    public double var() {
        return Math.exp(2 * params[MU] + Math.pow(params[SIGMA], 2.0d)) *
                (Math.exp(Math.pow(params[SIGMA], 2.0d)) - 1);
    }

    public double rand() {
        return Math.exp(RandomGenerator.random.nextGaussian() * params[SIGMA] + params[MU]);
    }

    public double nextInterPacketTime() {
        return rand();
    }

    public String getEquation() {
        return new String("f(x)= exp(-( (log(x)-mu) / (sqrt(2)*sigma) )^2)/(sqrt(2*pi)*sigma)");
        //+"mu = mean of a normally distributed variable sigma = standard deviation of a normally " +
        //"distributed variable  x = value at which to evaluate the function, must be > 0");
    }

    public void setParameters(HashMap parameters) {
        double mean;
        if (parameters.get("mean") != null) {
            mean = Double.parseDouble((String) parameters.get("mean"));
        } else {
            mean = 1.0d / Double.parseDouble((String) parameters.get("rate"));
        }
        double sigma = Double.parseDouble((String) parameters.get("sigma"));
        if (sigma > 0) {
            params[MU] = Math.log(mean) - (0.5 * Math.pow(sigma, 2));
            params[SIGMA] = sigma;
        }
    }

    public void estimateParameters(Inference inf) {
        double mu = 0, valor, sigma = 0;
        String nextString;
        try {
            inf.rewind();
            while ((nextString = inf.nextValue()) != null) {
                try {
                    valor = Double.parseDouble(nextString);
                    mu += Math.log(valor);
                } catch (NumberFormatException nfe) {
                }
            }
            long n = inf.getSampleSize();
            mu /= n;
            inf.rewind();
            while ((nextString = inf.nextValue()) != null) {
                try {
                    valor = Double.parseDouble(nextString);
                    sigma += Math.pow(Math.log(valor) - mu, 2);
                } catch (NumberFormatException nfe) {
                }
            }
            sigma /= n;
            sigma = Math.sqrt(sigma);
            params[MU] = mu;
            params[SIGMA] = sigma;
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /* //Old code with the testing unit and stuff YO!!!
    public static void main(String args[]){
    LogNormalDistribution dist = new LogNormalDistribution(3.5,4.3);
    LogNormalDistribution dist2 = new LogNormalDistribution();
    Log log = new Log("teste",false);
    for(int i=0; i<3000; i++){
    log.write(""+dist.rand());
    }
    log.close();
    try {
    DataReader dr = new DataReader(log.getFile());
    Inference inf = new Inference(dr,0);
    dist2.calculateMLE(inf);
    System.out.println(dist2.getParam(0)+" "+dist2.getParam(1));
    } catch (IOException e) {
    e.printStackTrace();
    }
    }*/
}
